<!-- 通用图片上传组件（上传多张图片。如：轮播图，详情图等） -->
<template>
  <div class="w-img-upload">
    <div class="w-main">
      <draggable :list="imageList" :group="group" animation="500" @start="() => (drag = true)" @end="handleDragEnd" class="w-continer-img" filter=".w-upload">
        <div class="w-content-img" v-for="(item, index) in imageList" :key="index">
          <!-- 解决可能存在undefined的情况 -->
          <img :src="IMG_URL + item" alt="图片" class="w-img" v-if="item" />
          <div class="icon-view-close-info" v-if="item">
            <el-icon class="icon-close" @click="handleShowImg(item)">
              <ZoomIn />
            </el-icon>
            <el-icon class="icon-close" v-if="showDeleteIcon" @click="handleDeleteImage(item)">
              <Delete />
            </el-icon>
          </div>
        </div>
        <div class="w-upload">
          <el-upload
            v-if="currentNum < limitNum && showUpload"
            accept=".png,.jpg,.jpeg,.gif"
            multiple
            :action="UPLOAD_URL"
            :data="uploadData"
            :show-file-list="false"
            :on-error="handleResourcesUploadError"
            :on-success="(response: IUpdateSuccessData, file: UploadFile) => handleResourcesUploadSuccess(response, file, successCallback)"
            :before-upload="file => handleBeforeUpload({ file, limit, customerBefore: handleCustomerBefore, errorCallback: handleErrorCallback })">
            <el-icon class="uploader-icon">
              <Plus />
            </el-icon>
          </el-upload>
        </div>
      </draggable>
    </div>
    <p>{{ suggestInfo }}</p>
  </div>
  <el-dialog title="查看" width="30%" append-to-body v-model="dialogImage">
    <img width="100%" :src="dialogImageUrl" alt="图片" />
  </el-dialog>
</template>

<script setup lang="ts">
import { VueDraggableNext as draggable } from "vue-draggable-next";
import { useUpload } from "@/hooks/useUpload";
import type { IUpdateSuccessData } from "@/types/upload";
import { IMG_URL, UPLOAD_URL } from "@/utils/config";
import type { UploadFile, UploadRawFile } from "element-plus";
interface Props {
  group: string /* 拖拽分组ID，同一个页面不相同即可 */;
  limit?: number /* 图片大小限制信息 */;
  limitNum?: number /* 图片数量限制信息 */;
  imageNum: number /* 图片的数量 */;
  suggestInfo?: string /* 提示信息 */;
  imageWidth?: number /* 修改图片宽度尺寸 */;
  imageHeight?: number /* 修改图片高度尺寸 */;
  showDeleteIcon?: boolean /* 是否显示删除按钮 */;
  showUpload?: boolean /* 是否显示上传按钮 */;
}
const { group, suggestInfo = "建议：尺寸750x750像素以上(等比例亦可)，大小1M以下，最多上传10张", limit = 1024, limitNum = 5, imageWidth = 178, imageHeight = 178, imageNum, showDeleteIcon = true, showUpload = true } = defineProps<Props>();
const disabledbtn = defineModel<boolean>("disabledbtn", { required: true });
const loading = defineModel<boolean>("loading", { required: true });
const imageList = defineModel<Array<string>>("imageList", { required: true });
const emit = defineEmits<{
  handleDeleteImage: [];
  successCallback: [];
}>();
/* 暴露一个方法给父组件使用 */
defineExpose({
  handleClearImage
});
let { uploadData, handleResourcesUploadSuccess, handleBeforeUpload, handleResourcesUploadError } = useUpload({ disabledbtn, loading });

const drag = ref<boolean>(false);
const dialogImage = ref(false);
/* 图片预览地址 */
const dialogImageUrl = ref("");
/* 当前上传数量 */
const currentNum = ref(imageNum);

/* 按顺序存储图片的uid或key */
const imageUIDOrKeyList = ref<Array<string>>([]);

watch(
  () => imageNum,
  () => {
    currentNum.value = imageNum;
    /* 可以这么写的原因：在父组件中imageList的赋值一定是在imageNum数据赋值之前 */
    /* 需要这么写的原因：编辑的情况下，有初始化的数据 */
    imageUIDOrKeyList.value = [...imageList.value];
  }
);
/* 清空图片，用于外部组件调用（这一步重要，会解决一些隐藏问题,imageNum和currentNum不一致的情况） */
function handleClearImage() {
  imageUIDOrKeyList.value = [];
  currentNum.value = 0;
}

/* 拖拽结束 */
function handleDragEnd() {}
/* 上传之前的自定义校验 */
function handleCustomerBefore(file: UploadRawFile) {
  currentNum.value++;
  if (currentNum.value > limitNum) {
    currentNum.value--;
    ElMessage.warning(`你无法继续上传，上传图片数量不超过${limitNum}张！`);
    return false;
  }
  imageUIDOrKeyList.value.push(file.uid.toString());
  return true;
}
/* 上传校验时失败的回调 */
function handleErrorCallback() {
  currentNum.value--;
  imageUIDOrKeyList.value.pop();
}
/* 删除图片 */
function handleDeleteImage(img: string) {
  const index = imageList.value.findIndex((item: string) => item == img);
  /* 可能不存在吗？应该不大可能，判断一下严谨点。 */
  if (index !== -1) {
    imageList.value.splice(index, 1);
  }
  /* 分开两次判断的原因：目标数组会拖拽 导致，两者的数据下标不同了 */
  const _i = imageUIDOrKeyList.value.findIndex((item: string) => item == img);
  if (_i !== -1) {
    imageUIDOrKeyList.value.splice(_i, 1);
  }
  currentNum.value--;
  emit("handleDeleteImage");
}
/* 显示图片 */
function handleShowImg(img: string) {
  dialogImage.value = true;
  dialogImageUrl.value = IMG_URL + img;
}
/* 上传成功回调 */
function successCallback(res: IUpdateSuccessData, file: UploadFile) {
  const index = imageUIDOrKeyList.value.findIndex(item => item == file.uid.toString());
  if (index !== -1) {
    /* 如果使用 splice 会存在i大于数组长度时 不会替换对应数组下标的值，而替换数组中最后一个值 */
    imageList.value[index] = "/" + res.key;
    imageUIDOrKeyList.value.splice(index, 1, "/" + res.key);
  }
  ElMessage.success("上传成功");
  emit("successCallback");
}
</script>

<style lang="scss" scoped>
/* 图片上传 start */
:deep(.el-upload--text) {
  width: inherit;
  height: inherit;
}
.w-main {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  // 定义变量
  $imageWidth: v-bind('imageWidth+"px"');
  $imageHeight: v-bind('imageHeight+"px"');
  $imageLineHeight: v-bind('imageHeight+"px"');
  .w-continer-img {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    .w-content-img {
      position: relative;
      width: $imageWidth;
      height: $imageHeight;
      .w-img {
        width: inherit;
        height: inherit;
        border-radius: 5px;
      }

      .icon-close {
        display: none;
      }
      &:hover .icon-view-close-info {
        border-radius: 5px;
        opacity: 1;
      }

      .icon-view-close-info {
        position: absolute;
        width: 100%;
        height: 100%;
        left: 0;
        top: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #fff;
        font-size: 20px;
        border-radius: 5px;
        background-color: rgba(0, 0, 0, 0.5);
        transition: opacity 0.3s;
        opacity: 0;
        .icon-close {
          display: flex;
          justify-content: center;
          align-items: center;
          width: 30px;
          height: 30px;
          cursor: pointer;
          & + .icon-close {
            margin-left: 10px;
          }
        }
      }
    }
  }
  .uploader-icon {
    width: $imageWidth;
    height: $imageHeight;
    line-height: $imageLineHeight;
    font-size: 28px;
    color: #8c939d;
    text-align: center;
  }
}
/* 图片上传 end */
</style>
